#include "Common.h"
#include "PhysicsSystem.h"
#include "UserAllocator.h"
#include <algorithm>
#include <NxPhysics.h>
#include <NxControllerManager.h>

using namespace Physics;

System::System()
	: mPhysicsSDK(NULL)
	, mControllerManager(NULL)
	, mSkinWidth(0.1f)
{
}

System::~System()
{
	Destory();
}

bool System::Init()
{
	mUserAllocator = new UserAllocator();

	mPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, mUserAllocator);
	mPhysicsSDK->setParameter(NX_SKIN_WIDTH, mSkinWidth);

	mControllerManager = NxCreateControllerManager(mUserAllocator);
	return true;
}

void System::Update(float elapsedTime)
{
	if (mControllerManager)
		mControllerManager->updateControllers();
}

void System::Destory()
{
	if (mControllerManager)
	{
		NxReleaseControllerManager(mControllerManager);
		mControllerManager = NULL;
	}

	if (mPhysicsSDK)
	{
		NxReleasePhysicsSDK(mPhysicsSDK);
		mPhysicsSDK = NULL;
	}

	SAFE_DELETE(mUserAllocator);
}

NxScene* System::CreateScene()
{
	if (mPhysicsSDK == NULL)
		return NULL;

	NxSceneDesc sceneDesc;
	sceneDesc.gravity = NxVec3(0.0f, -981.0f, 0.0f);
	NxScene* ret = mPhysicsSDK->createScene(sceneDesc);
	return ret;
}

void System::DestroyScene(NxScene* scene)
{
	if (mPhysicsSDK == NULL)
		return;

	mPhysicsSDK->releaseScene(*scene);
}

float System::RayCast(NxScene* scene, const Vector3& pos, const Vector3& dir, float maxDistance, void** userData, DWORD groups)
{
	if (scene == NULL || maxDistance <= 0)
		return maxDistance;

	NxRay worldRay;
	worldRay.orig.x	= pos.X;
	worldRay.orig.y	= pos.Y;
	worldRay.orig.z	= pos.Z;
	worldRay.dir.x	= dir.X;
	worldRay.dir.y	= dir.Y;
	worldRay.dir.z	= dir.Z;

	NxRaycastHit hit;
	NxShape* shape = scene->raycastClosestShape(worldRay, NX_ALL_SHAPES, hit, groups, maxDistance);
	if (shape)
	{
		if (userData != NULL)
			(*userData) = shape->getActor().userData;
		return hit.distance;
	}

	return maxDistance;
}
